package top.honlnk.service.impl.processor;

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import top.honlnk.common.constant.AdminConstant;
import top.honlnk.common.exception.CustomException;
import top.honlnk.pojo.dto.AdminGetKeyDTO;

import java.security.SecureRandom;
import java.time.LocalDate;
import java.time.LocalTime;

@Slf4j
public class GenerateAdminKeyProcessor {
    private static final String ALPHABET = "0123456789" +
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ".substring(0, 21); // 取前21个字符，即A-U 用于表示0-31的数字

    private static final String POSITION_ALPHABET = "0123456789" +
            "abcdefghijklmnopqrstuvwxyz" +
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ".substring(0, 24); // 共60个字符，取前24个字符，即A-X 用于表示0-59的数字

    private static final String NUM_ALPHABET = "_123456789" +
            "abcdefghijklmnopqrstuvwxyz" +
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ".substring(0, 25); // 共61个字符，取前25个字符，即A-Y 用于表示1-60的数字

    private static final String CHARACTERS ="0123456789" +
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; // 用于生成随机字符

    public String getKey(AdminGetKeyDTO adminGetKeyDTO) {
        // 生成表示天数差异的字符
        char DaysDifferenceChar = IntervalDays(adminGetKeyDTO);
        // 生成表示“表示天数差异字符”的所在整个key位置的字符
        char PositionChar = PositionChar();
        // 解析“PositionChar”字符表示的实际数字（0-9）
        int PositionNumber = PositionCharForPositionNumber(PositionChar);
        // 生成整个密钥的前半部分
        String user_key = PositionUserKey(DaysDifferenceChar, PositionChar, PositionNumber);

        // 生成两个随机字符+当前时间戳对应的自定义字符串，作为整个密钥的后半部分。
        GenerateSubmissionIdProcessor generateSubmissionIdProcessor = new GenerateSubmissionIdProcessor();
        String time_string = generateSubmissionIdProcessor.encode();

        // 拼接完整的密钥：对值班表进行一系列操作时用到的密钥
        // 是由用户密钥加上管理员要求的每个学生值班次数dates和每节课推荐的值班人数 + 当前时间戳
        return PositionAdminKey(user_key, adminGetKeyDTO) + time_string;
    }

    private @NotNull String PositionAdminKey(String user_key, @NotNull AdminGetKeyDTO adminGetKeyDTO) {
        int num = adminGetKeyDTO.getNum();
        int classes = adminGetKeyDTO.getClasses();
        if (num > 60 || classes > 60) {
            throw new CustomException(AdminConstant.NUM_CLASS_EXCEED_60);
        } else if (num <= 0 || classes <= 0) {
            throw new CustomException(AdminConstant.NUM_CLASS_LESS_0);
        }
        user_key = user_key + NUM_ALPHABET.charAt(classes) + NUM_ALPHABET.charAt(num);
        return user_key;
    }

    private @NotNull String generateRandomCharacter() {
        SecureRandom secureRandom = new SecureRandom();
        int randomIndex = secureRandom.nextInt(CHARACTERS.length());
        char randomCharacter = CHARACTERS.charAt(randomIndex);
        return Character.toString(randomCharacter);
    }

    private @NotNull String PositionUserKey(char DaysDifferenceChar, char PositionChar, int PositionNumber) {
        StringBuilder sb = new StringBuilder();
        // 从第一个字符开始，跳过PositionNumber个位置
        for (int i = 0; i < PositionNumber; i++) {
            sb.append(generateRandomCharacter());
        }
        // 在指定位置插入天数差异字符
        sb.append(DaysDifferenceChar);

        // 填充剩余位置
        for (int i = sb.length(); i < 10; i++) {
            sb.append(generateRandomCharacter());
        }

//      将sb转为普通字符串
        String sbString = sb.toString();

        return PositionChar + sbString; // 总长度为11 0是表示“PositionChar”所在位置的字符 1-10是真正的key
    }

    private int PositionCharForPositionNumber(char positionChar) {
        int number = POSITION_ALPHABET.indexOf(positionChar);
        return number % 10;
    }

    private char PositionChar() {
        // 随机生成一个位置字符
//      随机生成一个1到60的数字
        int randomNumber = (int) (Math.random() * 60) + 1;
        //        log.info("positionChar: {}", positionChar);
        return NUM_ALPHABET.charAt(randomNumber);
    }

    private char IntervalDays(AdminGetKeyDTO adminGetKeyDTO) {
        LocalDate currentDate = LocalDate.now();
        LocalDate inputDate = adminGetKeyDTO.getDate();
        LocalTime currentTime = LocalTime.now();

        if (currentTime.isAfter(LocalTime.of(20, 0)) && inputDate.equals(currentDate)) {
            throw new CustomException(AdminConstant.DATE_LATER_THAN_20);
        }

        if (inputDate.isAfter(currentDate.plusDays(31))) {
            throw new CustomException(AdminConstant.DATE_EXCEED_31);
        } else if (inputDate.isBefore(currentDate)) {
            throw new CustomException(AdminConstant.DATE_EARLIER_TODAY);
        }
        long daysDifference = inputDate.toEpochDay() - currentDate.toEpochDay();
        return ALPHABET.charAt((int) (daysDifference % ALPHABET.length()));
    }

}
